package com.hung.triple.registry.nacos;

import com.alibaba.nacos.api.naming.pojo.Instance;
import com.google.common.base.Stopwatch;
import com.hung.triple.bootstrap.TripleProviderConfig;
import com.hung.triple.common.enums.RpcError;
import com.hung.triple.common.exception.TripleRpcException;
import com.hung.triple.common.utils.CommonUtils;
import com.hung.triple.consumer.ConsumerBeanInfo;
import com.hung.triple.context.RpcRuntimeContext;
import com.hung.triple.registry.ProviderAddressPool;
import com.hung.triple.registry.ProviderBeanInfo;
import com.hung.triple.registry.Register;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * @author Hung
 * @date 2022/9/24 23:19
 */
@Slf4j
public class NacosServiceRegistry implements Register {

    /**
     * 服务中心集群ip tag:http://ip:port
     */
    private List<String> clusterIps;

    /**
     * 本机地址ip tag:http://ip:port
     */
    private String localIp = TripleProviderConfig.getLocalIp();
    private String localPort = TripleProviderConfig.getLocalPort();

    public NacosServiceRegistry(List<String> clusterIps) {
        this.clusterIps = clusterIps;
    }

    @Override
    public void register(List<ProviderBeanInfo> beans) throws Exception {
        Stopwatch sw = Stopwatch.createStarted();

        InetSocketAddress inetSocketAddress = new InetSocketAddress(localIp, Integer.parseInt(localPort));
        for (ProviderBeanInfo bean : beans) {
            Supplier<Void> executor = () -> {
                //获取服务器提供者的信息
                String group = bean.getGroupName();
                String serviceName = bean.getServiceName();
                String version = bean.getVersion();
                bean.addInfo("ip", localIp);
                String path = String.format("/%s/%s/%s/provider/%s", group, serviceName, version, localIp);
                try {
                    //注册服务到Nacos和本地Runtime缓存
                    NacosUtil.registerService(bean, inetSocketAddress);
                    RpcRuntimeContext.cacheProviderConfig(bean);
                    log.info("[Triple] service({}#{}:{}) registered success in path({}).", group, serviceName, version, path);
                } catch (Exception e) {
                    log.error("注册服务时有错误发生:", e);
                    throw new TripleRpcException(RpcError.REGISTER_SERVICE_FAILED.getMessage());
                }
                return null;
            };
            CommonUtils.executeWithRetry(executor, 3, 100);
        }
        sw.stop();
        log.info("[Triple] all service registration by nacos successful, using {}ms.", sw.elapsed(TimeUnit.MILLISECONDS));
    }

    @Override
    public void discover(List<ProviderBeanInfo> beans) throws Exception {
        for (ProviderBeanInfo bean : beans) {
            String group = bean.getGroupName();
            String serviceName = bean.getServiceName();
            String version = bean.getVersion();

            List<Instance> instances = NacosUtil.getAllInstance(serviceName, group, version);
            if (instances.size() == 0) {
                log.error("找不到对应的服务: " + serviceName);
                throw new TripleRpcException(RpcError.SERVICE_NOT_FOUND.getMessage());
            }
            List<String> providerIps = instances.stream().map(Instance::getIp).collect(Collectors.toList());
            log.info("[Triple] {}#{}:{}'s provider ip list:{}", group, serviceName, "", providerIps);
            ProviderAddressPool.add(group, serviceName, version, providerIps);

        }
    }

    @Override
    public void subscribe(ConsumerBeanInfo consumerBeanInfo) {
        NacosUtil.subscribe(consumerBeanInfo);
    }

    @Override
    public void deSubscribe(List<ConsumerBeanInfo> consumerBeanInfos) {
        consumerBeanInfos.forEach(this::deSubscribe);
    }

    @Override
    public void deSubscribe(ConsumerBeanInfo consumerBeanInfo) {
        NacosUtil.deSubscribe(consumerBeanInfo);
    }

    @Override
    public void deRegister(List<ProviderBeanInfo> beans) {
        beans.forEach(this::deRegister);
    }

    @Override
    public void deRegister(ProviderBeanInfo bean) {
        NacosUtil.deRegisterService(bean.getServiceName(), bean.getGroupName(), bean.getVersion(), bean.getInstance());
    }

}
